Typical HAL FLASH use cases for migration

This section provides information on typical HAL FLASH use cases during migration.

Note

  • In HAL1, the FLASH driver manages Flash memory operations.

  • In HAL2, depending on the device family, memory operations are managed either by the FLASH driver or by the NVM driver.

  • In this migration guide, when the same use case exists in both HAL2 FLASH and HAL2 NVM, the migration is illustrated using the HAL2 FLASH API. The same rule applies to the corresponding HAL2 NVM API.

  • Example: in HAL1, HAL_FLASH_IRQHandler() function becomes:

    • HAL_FLASH_IRQHandler() in HAL2 FLASH driver.

    • HAL_NVM_IRQHandler() in HAL2 NVM driver.

  • For NVM-based families, the NVM driver provides services similar to those of FLASH with minor differences:

    • There is no erase step for NVM memories.

      • Erase-by-address, page erase, bank erase, and FLASH mass erase remain FLASH-specific services.

      • For NVM-based families, the only erase service that may be exposed is OBK erase, when that feature exists.

The HAL FLASH driver has been restructured into two main components:

  • FLASH IO (flash.h/c):

    • Encapsulates FLASH IO operations such as programming, erasing, and retrieving flash information.

    • Supports buffer-based programming (unlike HAL1 single FLASH word writes).

    • Introduces a handle-based approach for managing flash processes in polling and interrupt modes.

    • Provides granular erase APIs (erase by address, page, bank, or mass erase).

    • Enables optional callback registration for asynchronous event handling.

  • FLASH ITF (flash_itf.h/c):

    • Focuses on access control features, option bytes management, and interrupt enabling.

    • Replaces global option byte APIs with dedicated, feature-specific unitary APIs.

  • Additionally, a Low-Layer (LL) FLASH driver (ll_flash.h) is available for lightweight, direct register-access APIs.


Use case: User application program in polling mode

Use case applicable in HAL2 for both FLASH and NVM.

Note

In HAL2, FLASH programming APIs have been streamlined with clearer and more flexible parameters. The legacy TypeProgram, FlashAddress, and DataAddress pattern is replaced by buffer-based APIs.

HAL2 provides specialized programming APIs like HAL_FLASH_{area}_ProgramByAddr for specific FLASH regions such as OTP, EDATA, and OBK.

  • HAL_FLASH_ProgramByAddr to program a user flash area

  • HAL_FLASH_OTP_ProgramByAddr to program an OTP flash area

  • HAL_FLASH_EDATA_ProgramByAddr to program an EDATA flash area

  • HAL_FLASH_OBK_ProgramByAddr to program an OB Key flash area

Sequence diagram

User application program in polling mode (HAL1)

@startuml
title: User application program in polling mode (HAL1)

hide footbox
participant "User Application" as User
participant "FLASH" as FLASH

group #snow FLASH Unlock
    User -> FLASH : HAL_FLASH_Unlock()
end

group #snow FLASH Program Operation (loop)
    loop while Address < FLASH_USER_END_ADDR
        User -> FLASH : HAL_FLASH_Program(FLASH_TYPEPROGRAM_GRANULARITY, Address, GRANULARITY)
        alt Program success (HAL_OK)
            note right: Increment Address by 32
            User --> User : Address = Address + 32
        else Program failure
            User --> User : Handle error or retry
        end
    end
end

group FLASH Lock (recommended)
    User -> FLASH : HAL_FLASH_Lock()
end

@enduml

Important

In HAL2, the programming APIs handle the entire data buffer in a single call, eliminating the need for explicit looping over FLASH units.

Code snippet

HAL1

HAL2

uint32_t Address = FLASH_USER_START_ADDR;
const uint32_t QUADWORD[4] = {
  0x12345678, 0x87654321, 0x12344321, 0x56788765
};

/* Unlock the FLASH to enable the FLASH control register access */
HAL_FLASH_Unlock();

while (Address < FLASH_USER_END_ADDR)
{
  /* Program FLASH memory */
  if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD,
                       Address,
                       (uint32_t)QUADWORD) == HAL_OK)
  {
    Address = Address + 32U; /* increment for the next FLASH word */
  }
}

/* Lock the FLASH to disable the FLASH control register access */
HAL_FLASH_Lock();
hal_flash_handle_t flash_handle;
uint32_t address = FLASH_START_ADDR;

/* FLASH initialization */
HAL_FLASH_Init(&flash_handle, HAL_FLASH);

/* Unlock the FLASH to enable the FLASH control register access */
HAL_FLASH_ITF_Unlock(HAL_FLASH);

#if defined (USE_HAL_FLASH_PROGRAM_BY_ADDR) && (USE_HAL_FLASH_PROGRAM_BY_ADDR == 1U)

/* Set the programming mode according to the FLASH granularity */
HAL_FLASH_SetProgrammingMode(&flash_handle, HAL_FLASH_PROGRAM_QUADWORD);

/* Program FLASH memory using the granularity size in bytes */
/* area = User flash, EDATA, OTP */
HAL_FLASH_ProgramByAddr(&flash_handle, address, data_buf, QUADWORD_SIZE_BYTES, TIMEOUT);

#endif /* USE_HAL_FLASH_PROGRAM_BY_ADDR */

/* Lock the FLASH to disable the FLASH control register access */
HAL_FLASH_ITF_Lock(HAL_FLASH);

/* FLASH deinitialization */
HAL_FLASH_DeInit(&flash_handle);

Key changes

  1. Buffer-based programming replaces the HAL1 per-granule loop (single call programs a full buffer).

  2. Programming APIs are specialized per area in HAL2 (USER/OTP/EDATA/OBK) via HAL_FLASH_{area}_ProgramByAddr.

  3. HAL2 uses an explicit handle + IO/ITF split (init/deinit in IO, unlock/lock in ITF).


Use case: User application program in interrupt mode

Use case applicable in HAL2 for both FLASH and NVM.

Important

In HAL1, the FLASH driver provided fixed callbacks without explicit registration due to its stateless design.

HAL2 introduces a FLASH handle and allows users to register callbacks (for example, HAL_FLASH_RegisterProgramCpltCallback) when callback registration is enabled, enabling more modular event handling.

Sequence diagram

User application program in interrupt mode (HAL1)

@startuml
title: User application program in interrupt mode (HAL1)

hide footbox
participant "User Application" as User
participant "Cortex" as Cortex
participant "FLASH" as FLASH
participant "NVIC" as NVIC
participant "HW" as HW

group #snow Initialization
   User -> Cortex : HAL_NVIC_SetPriority(FLASH_IRQn)
   User -> Cortex : HAL_NVIC_EnableIRQ(FLASH_IRQn)
end

group #snow FLASH Unlock
   User -> FLASH : HAL_FLASH_Unlock()
end

group #snow FLASH Programming (Interrupt Mode)
   User -> FLASH : HAL_FLASH_Program_IT(FLASH_TYPEPROGRAM_GRANULARITY, Address, GRANULARITY)
   loop while Address < EndAddress
      HW -> NVIC : Generate FLASH interrupt
      NVIC -> FLASH : FLASH_IRQHandler()
      FLASH -> User : HAL_EndOfOperationCallback()
      alt Program success
         User -> User : Increment Address by offset
         User -> FLASH : HAL_FLASH_Program_IT(...)
      else Program failure
         User -> User : Handle error
      end
   end
end

group #snow FLASH Lock
   User -> FLASH : HAL_FLASH_Lock()
end

@enduml

Code snippet

HAL1

HAL2

uint32_t Address = FLASH_USER_START_ADDR;
const uint32_t QUADWORD[4] = {
  0x12345678, 0x87654321, 0x12344321, 0x56788765
};

/* Unlock the FLASH to enable the FLASH control register access */
HAL_FLASH_Unlock();

/* FLASH interrupt user callback */
void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue)
{
  /* This variable counts the number of Program/Erase Operations */
  EndofProgram++;
}

while (Address < EndAddress)
{
  EndofProgram = 0;
  status = HAL_FLASH_Program_IT(FLASH_TYPEPROGRAM_QUADWORD, Address, (uint32_t)QUADWORD);
  while (!EndofProgram) { }
  if (status == HAL_OK)
  {
    Address = Address + offset;
  }
}

/* Lock the FLASH to disable the FLASH control register access */
HAL_FLASH_Lock();
hal_flash_handle_t flash_handle;
uint32_t address = FLASH_START_ADDR;

/* FLASH initialization */
HAL_FLASH_Init(&flash_handle, HAL_FLASH);

/* Unlock the FLASH to enable the FLASH control register access */
HAL_FLASH_ITF_Unlock(HAL_FLASH);

#if defined (USE_HAL_FLASH_PROGRAM_BY_ADDR) && (USE_HAL_FLASH_PROGRAM_BY_ADDR == 1U)

#if defined (USE_HAL_FLASH_REGISTER_CALLBACKS) && (USE_HAL_FLASH_REGISTER_CALLBACKS == 1)
HAL_FLASH_RegisterProgramCpltCallback(&flash_handle, ProgramCpltCallback);
#endif /* USE_HAL_FLASH_REGISTER_CALLBACKS */

/* Set the programming mode according to the FLASH granularity */
HAL_FLASH_SetProgrammingMode(&flash_handle, HAL_FLASH_PROGRAM_QUADWORD);

/* Start interrupt programming */
HAL_FLASH_{area}_ProgramByAddr_IT(&flash_handle, address, data_buf, QUADWORD_SIZE_BYTES);

#endif /* USE_HAL_FLASH_PROGRAM_BY_ADDR */

/* Lock the FLASH to disable the FLASH control register access */
HAL_FLASH_ITF_Lock(HAL_FLASH);

/* FLASH deinitialization */
HAL_FLASH_DeInit(&flash_handle);

Key changes

  1. HAL2 uses callback registration (when enabled) instead of relying only on fixed weak callbacks.

  2. HAL2 interrupt programming APIs are handle-based and area-specialized (HAL_FLASH_{area}_ProgramByAddr_IT).

  3. NVIC configuration remains required, but the interrupt handler and completion callback flow are explicit.


Use case: User application erase in polling mode

Use case applicable in HAL2 only for FLASH.

Important

When using HAL_FLASH_EraseByAddr(), note that because the hardware flash controller erases entire fixed-size pages, if the start or end address falls within a page, the whole page(s) containing those addresses will be erased. The erased area can therefore be larger than the specified range.

Sequence diagram

User application erase in polling mode (HAL1)

@startuml
title: User application erase in polling mode (HAL1)

hide footbox
participant "User Application" as User
participant "FLASH" as FLASH

group #snow FLASH Unlock
   User -> FLASH : HAL_FLASH_Unlock()
end

group #snow FLASH Erase Operation
   User -> User : Fill EraseInitStruct\n- TypeErase = FLASH_TYPEERASE_<TARGET>\n- Banks = BankNumber\n- Sector/Page = First\n- NbSectors/NbPages = Count
   note right: <TARGET> can be SECTORS, MASSERASE, OBK_ALT
   User -> FLASH : HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError)
end

group FLASH Lock (recommended)
   User -> FLASH : HAL_FLASH_Lock()
end

@enduml

Code snippet

HAL1

HAL2

uint32_t FirstPage = GetPage(FLASH_USER_NS_START_ADDR);
uint32_t NbOfPages = GetPage(FLASH_USER_NS_END_ADDR) - FirstPage + 1;

/* Unlock the FLASH to enable the FLASH control register access */
HAL_FLASH_Unlock();

/* Fill EraseInit structure */
EraseInitStruct.TypeErase     = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Banks         = BankNumber;
EraseInitStruct.Page          = FirstPage;
EraseInitStruct.NbPages       = NbOfPages;

HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);

/* Lock the FLASH to disable the FLASH control register access */
HAL_FLASH_Lock();
hal_flash_handle_t flash_handle;
uint32_t page_number;
uint32_t number_of_pages;
uint32_t timeout;

/* FLASH initialization */
HAL_FLASH_Init(&flash_handle, HAL_FLASH);

/* Unlock the FLASH to enable the FLASH control register access */
HAL_FLASH_ITF_Unlock(HAL_FLASH);

#if defined (USE_HAL_FLASH_ERASE_PAGE) && (USE_HAL_FLASH_ERASE_PAGE == 1U)
HAL_FLASH_ErasePage(&flash_handle, HAL_FLASH_BANK_1, page_number, number_of_pages, timeout);
#endif /* USE_HAL_FLASH_ERASE_PAGE */

/* Lock the FLASH to disable the FLASH control register access */
HAL_FLASH_ITF_Lock(HAL_FLASH);

/* FLASH deinitialization */
HAL_FLASH_DeInit(&flash_handle);

Key changes

  1. HAL1 uses a filled erase init structure; HAL2 provides granular erase APIs (ByAddr/Page/Bank/Mass) with explicit parameters.

  2. HAL2 separates operational services (IO) from access control (ITF) using distinct unlock/lock APIs.

  3. Address-range erase in HAL2 remains page-aligned and may erase a wider area than requested.


Use case: User application erase in interrupt mode

Use case applicable in HAL2 only for FLASH.

Important

Interrupt-mode erase operations in HAL2 are granular and callback-driven. Instead of passing a filled erase init structure ( FLASH_EraseInitTypeDef) like HAL1, you invoke a dedicated *_IT API (for example, HAL_FLASH_EraseByAddr_IT) whose parameters fully describe the target.

When callback registration is enabled, register the appropriate completion callback (for example, HAL_FLASH_RegisterEraseByAddrCpltCallback). Page alignment still applies: an address range intersecting a page causes the whole page(s) to be erased.

Sequence diagram

User application erase in interrupt mode (HAL1)

@startuml
title: User application erase in interrupt mode (HAL1)

hide footbox
participant "User Application" as User
participant "FLASH" as FLASH
participant "NVIC" as NVIC
participant "HW" as HW

group #snow NVIC Configuration
   User -> NVIC : HAL_NVIC_SetPriority(FLASH_IRQn)
   User -> NVIC : HAL_NVIC_EnableIRQ(FLASH_IRQn)
end

group #snow FLASH Unlock
   User -> FLASH : HAL_FLASH_Unlock()
end

group #snow Prepare Erase (struct)
   User -> User : Fill EraseInitStruct\n- TypeErase = FLASH_TYPEERASE_<TARGET>\n- Banks = BankNumber\n- Sector = FirstSector\n- NbSectors = NbOfSectors
   note right: <TARGET> can be SECTORS, MASSERASE, OBK_ALT
end

group #snow Start Erase IT
   User -> FLASH : HAL_FLASHEx_Erase_IT(&EraseInitStruct)
end

group #snow Interrupt-driven progress
   HW -> FLASH : FLASH IRQ
   FLASH -> User : HAL_FLASH_EndOfOperationCallback()
end

group FLASH Lock (recommended)
   User -> FLASH : HAL_FLASH_Lock()
end

@enduml

Code snippet

HAL1

HAL2

uint32_t FirstPage = GetPage(FLASH_USER_START_ADDR);
uint32_t NbOfPages = GetPage(FLASH_USER_END_ADDR) - FirstPage + 1;
FLASH_EraseInitTypeDef EraseInitStruct;

/* NVIC configuration */
HAL_NVIC_SetPriority(FLASH_IRQn);
HAL_NVIC_EnableIRQ(FLASH_IRQn);

/* Unlock FLASH */
HAL_FLASH_Unlock();

/* Prepare erase parameters */
EraseInitStruct.TypeErase  = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Banks      = BankNumber;
EraseInitStruct.Sector     = FirstPage;
EraseInitStruct.NbSectors  = NbOfPages;

/* Start interrupt erase */
HAL_StatusTypeDef status = HAL_FLASHEx_Erase_IT(&EraseInitStruct);
if (status != HAL_OK) { /* handle error */ }

/* Erase completion handled in callback */
void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue)
{
  /* Application can update progress metrics here */
}

/* Lock FLASH */
HAL_FLASH_Lock();
hal_flash_handle_t flash_handle;
uint32_t start_addr = FLASH_START_ADDR;
uint32_t length_bytes = ERASE_LENGTH_BYTES;

/* NVIC configuration */
HAL_CORTEX_NVIC_SetPriority(FLASH_IRQn);
HAL_CORTEX_NVIC_EnableIRQ(FLASH_IRQn);

/* Initialization */
HAL_FLASH_Init(&flash_handle, HAL_FLASH);

/* Unlock interface */
HAL_FLASH_ITF_Unlock(HAL_FLASH);

/* Optional: register completion callback */
#if defined (USE_HAL_FLASH_REGISTER_CALLBACKS) && (USE_HAL_FLASH_REGISTER_CALLBACKS == 1)
HAL_FLASH_RegisterEraseByAddrCpltCallback(&flash_handle, EraseByAddrDone);
#endif

/* Start interrupt erase (address-range) */
hal_status_t status = HAL_FLASH_EraseByAddr_IT(&flash_handle, start_addr, length_bytes);
if (status != HAL_OK) { /* handle error */ }

void EraseByAddrDone(hal_flash_handle_t *h, uint32_t flash_addr, uint32_t size_byte)
{
  /* flash_addr: first erased page address, size_byte: total erased bytes */
}

/* Lock interface */
HAL_FLASH_ITF_Lock(HAL_FLASH);

/* De-initialize */
HAL_FLASH_DeInit(&flash_handle);

Key changes

  1. HAL2 provides multiple *_IT erase APIs (ByAddr/Page/Bank/Mass) instead of a single struct-based entry point.

  2. Completion is reported via handle-associated callbacks (optional registration), rather than only weak callbacks.

  3. NVIC setup remains required, but the handler/callback chain is clarified by the IO/ITF separation.


Use case: User application option-bytes configuration

Use case applicable in HAL2 for both FLASH and NVM.

Note

  • Option bytes may differ from one series to another. However, the sequence and programming model remain the same.

Important

In HAL1, option bytes are programmed through a single monolithic FLASH_OBProgramInitTypeDef structure passed to HAL_FLASHEx_OBProgram(), requiring the application to populate many fields even when configuring one feature.

HAL2 removes this composite struct in favor of feature-specific unitary APIs (for example, HAL_FLASH_ITF_OB_SetRDPLevel()) followed by an explicit HAL_FLASH_ITF_OB_Program() and HAL_FLASH_ITF_OB_Launch() sequence.

In HAL2, HAL_FLASH_ITF_OB_Unlock() unlocks OB control (instead of two calls in HAL1), and the launch step explicitly triggers the option-byte loader reset.

Sequence diagram

User application option-bytes configuration (HAL1)

@startuml
title: User application option-bytes configuration (HAL1)

hide footbox
participant "User Application" as User
participant "FLASH" as Flash
participant "System" as Sys

group #snow Initialization
   User -> Sys : HAL_Init()
   User -> Sys : SystemClock_Config()
   note right: FLASH latency updated according to bus frequency
end

group #snow FLASH Unlock
   User -> Flash : HAL_FLASH_Unlock()
end

group #snow FLASH Option Bytes Unlock
   User -> Flash : HAL_FLASH_OB_Unlock()
end

group #snow Prepare Option Bytes Configuration
   User -> User : Fill FLASH_OBProgramInitTypeDef structure
end

group #snow FLASH Option Bytes Programming
   User -> Flash : HAL_FLASHEx_OBProgram(&FLASH_OBInitStruct)
end

group #snow FLASH Option Bytes Lock
   User -> Flash : HAL_FLASH_OB_Lock()
end

group #snow FLASH Lock (recommended)
   User -> Flash : HAL_FLASH_Lock()
end

@enduml

Code snippet

HAL1

HAL2

/* Example: Configure RDP level 1 and BOR level 2 (HAL1) */
FLASH_OBProgramInitTypeDef ob_cfg;
FLASH_OBProgramInitTypeDef ob_curr;

/* Unlock main FLASH and option bytes interface */
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();

/* (Optional) Retrieve current option bytes for comparison */
HAL_FLASHEx_OBGetConfig(&ob_curr);

/* Select the option bytes to modify */
ob_cfg.OptionType = OPTIONBYTE_RDP | OPTIONBYTE_BOR;

/* Configure the desired option bytes */
ob_cfg.RDPLevel = OB_RDP_LEVEL_1;
ob_cfg.BORLevel = OB_BOR_LEVEL_2;

/* Program the selected option bytes */
if (HAL_FLASHEx_OBProgram(&ob_cfg) != HAL_OK)
{
  /* handle error */
}

/* Generate option byte reload / reset */
HAL_FLASH_OB_Launch();

/* Lock main FLASH and option bytes */
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
/* Example: Configure RDP level 1 and BOR level 2 (HAL2) */

/* Unlock option bytes interface */
HAL_FLASH_ITF_OB_Unlock(HAL_FLASH);

if (HAL_FLASH_ITF_OB_SetRDPLevel(HAL_FLASH, HAL_FLASH_OB_RDP_LEVEL_1) != HAL_OK)
{
  /* handle error */
}

if (HAL_FLASH_ITF_OB_SetBORLevel(HAL_FLASH, HAL_FLASH_OB_BOR_LEVEL_2) != HAL_OK)
{
  /* handle error */
}

/* Program buffered option byte changes */
if (HAL_FLASH_ITF_OB_Program(HAL_FLASH) != HAL_OK)
{
  /* handle error */
}

/* Launch to reload the new option bytes (may trigger reset) */
HAL_FLASH_ITF_OB_Launch(HAL_FLASH);

/* Lock option bytes control */
HAL_FLASH_ITF_OB_Lock(HAL_FLASH);

Key changes

  1. Unitary, feature-specific setters replace the monolithic HAL1 option-byte configuration struct.

  2. Option-byte writeback and reload are explicit in HAL2 (OB_Program + OB_Launch).

  3. Option-byte control is confined to the ITF layer, clarifying separation of concerns.


Use case: User application program for OBK area in polling mode

Use case applicable in HAL2 for both FLASH and NVM.

Sequence diagram

User application program OBK area in polling mode (HAL1)

@startuml
title: User application program OBK area in polling mode (HAL1)

hide footbox
participant "User Application" as User
participant "FLASH" as Flash
participant "SBS" as SBS

group #snow FLASH Unlock
   User -> Flash : HAL_FLASH_Unlock()
end

group #Moccasin SBS HDPL Configuration and Unlock OBK area
   User -> SBS : HAL_SBS_SetOBKHDPL()
   note right: x : HDP Levels
   User -> Flash : HAL_FLASHEx_OBK_Unlock()
end

group #snow FLASH Erase Operation
   User -> Flash : HAL_FLASHEx_Erase(OBK alternate area)
   note right: Erase OBK alternate area sectors
end

group #snow FLASH Swap Operation
   User -> Flash : HAL_FLASHEx_OBK_Swap()
   note right: Swap OBK sectors between current and alternate
end

group #snow FLASH Program Operation (loop)
   loop while Address < HDPLx_END_ADDRESS
      User -> Flash : HAL_FLASH_Program(FLASH_TYPEPROGRAM_GRANULARITY_ALT, Address, Granularity)
      alt Program success (HAL_OK)
         User --> User : Address = Address + offset
      else Program failure
         User --> User : Handle error or retry
      end
   end
end

group #Moccasin FLASH Lock OBK area
   User -> Flash : HAL_FLASHEx_OBK_Lock()
end

group #snow Lock FLASH (recommended)
   User -> Flash : HAL_FLASH_Lock()
end

@enduml

Code snippet

HAL1

HAL2

uint32_t Address = HDPLx_START_ADDRESS;
FLASH_EraseInitTypeDef EraseInitStruct;
const uint32_t QUADWORD[4] = {
  0x12345678, 0x87654321, 0x12344321, 0x56788765
};

HAL_FLASH_Unlock();

/* Increment OBK-HDPL value */
HAL_SBS_SetOBKHDPL(SBS_OBKHDPL_INCR_x);

/* Unlock OBK area */
HAL_FLASHEx_OBK_Unlock();

/* Erase the OBK alternate area */
EraseInitStruct.TypeErase = FLASH_TYPEERASE_OBK_ALT;
HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);

/* Swap OBK: HDPLx data copied from current to alternate OBK sector */
HAL_FLASHEx_OBK_Swap(FLASH_OBK_SWAP_OFFSET_HDPLx);

while (Address < HDPLx_END_ADDRESS)
{
  if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD_OBK, Address, (uint32_t)QUADWORD) == HAL_OK)
  {
    Address = Address + offset;
  }
}

HAL_FLASH_Lock();
HAL_FLASHEx_OBK_Lock();
hal_flash_handle_t flash_handle;
uint32_t Address = HDPLx_START_ADDRESS;
const uint32_t QUADWORD[4] = {
  0x12345678, 0x87654321, 0x12344321, 0x56788765
};

HAL_FLASH_Init(&flash_handle, HAL_FLASH);
HAL_FLASH_ITF_Unlock(HAL_FLASH);

/* Increment OBK-HDPL value */
HAL_SBS_SetHDPLevelValue(HAL_SBS_OBKHDPL_INCR_x);

/* Unlock OBK area */
HAL_FLASH_ITF_OBK_Unlock(HAL_FLASH);

/* OBK mass erase */
HAL_FLASH_OBK_MassErase(HAL_FLASH, TIMEOUT);

/* OBK swap pages */
HAL_FLASH_OBK_SwapPages(HAL_FLASH, HAL_FLASH_ITF_OBK_AREA_x);

/* Set the programming mode according to the FLASH OBK granularity */
HAL_FLASH_SetProgrammingMode(&flash_handle, HAL_FLASH_PROGRAM_QUADWORD);

HAL_FLASH_OBK_ProgramByAddr(&flash_handle, HAL_FLASH_OBK_CURRENT_PAGE, Address, QUADWORD, 4, TIMEOUT);

HAL_FLASH_ITF_OBK_Lock(HAL_FLASH);
HAL_FLASH_ITF_Lock(HAL_FLASH);

Key changes

  1. HAL2 provides dedicated OBK APIs (unlock/swap/mass erase/program) split across ITF and IO.

  2. OBK programming in HAL2 is handle-based and uses buffer-oriented *_ProgramByAddr APIs.

  3. The SBS configuration step remains, but OBK control is better isolated from standard user FLASH services.